package com.ruoyi.common.core.redis;

import java.util.*;
import java.util.concurrent.TimeUnit;

import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;


/**
 * spring redis 工具类
 *
 * @author ruoyi
 **/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }


    /**
     *  将 key 中储存的数字值减一。
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECR 操作。
     * @param key  O（1）
     *    返回总数
     */
    public <T> Long setCacheDecrement(final T key)
    {
        Long decrement = redisTemplate.opsForValue().decrement(key);
        return decrement;
    }


    /**
     *  将 key 中储存的数字值增一。
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * @param key  O（1）
     *  返回总数
     */
    public <T> Long setCacheIncrement(final T key)
    {
        Long decrement = redisTemplate.opsForValue().increment(key);
        return decrement;
    }

    /**
     *  将 key 中储存的数字值加 value
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * @param key  O（1）
     *  返回总数
     */
    public <T> Long setCacheIncrement(final T key,final long value)
    {


        Long increment = redisTemplate.opsForValue().increment(key, value);
        return increment;
    }


    /**
     * bigmap位图
     * @param key 键
     * @param value 位图下标
     * @param bool true为1 false为0
     * @param <T> 类型
     *
     */
    public Boolean setBig(final String key, final long value,boolean bool)
    {

        Boolean aBoolean = redisTemplate.opsForValue().setBit(key, value, bool);
        return aBoolean;
    }

    /**
     * 判断元素在bigmap中是否存在
     * @param key 键
     * @param value 元素
     * @param <T>
     * @return bool
     */
    public <T> Boolean getBig(final String key, final long value)
    {

        Boolean bit = redisTemplate.opsForValue().getBit(key, value);
        return bit;
    }


    /**
     * 获取指定下标范围内的所有为1的值 十进制显示 如 1000 为8
     * @param key
     * @param value
     * @return
     */
    public long getBigsCount(final String key, final int start,final int end)
    {

//        start和end包头不包尾
        // key,(get offset offset)子命令
        List<Long> list = redisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create().
                        get(BitFieldSubCommands.BitFieldType.unsigned(start)).valueAt(end)
        );

        if (list==null || list.isEmpty())
        {
            return 0;
        }
        Long aLong = list.get(0);
        if (aLong==null)
        {
            return 0;
        }
        return list.get(0);
    }


    /**
     * 从0开始往后取
     * @param key
     * @param start
     * @param end
     * @return
     */
    public long getReverBigsCount(final String key, final int start,final int end)
    {

        // key,(get offset offset)子命令
        List<Long> list = redisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create().
                        get(BitFieldSubCommands.BitFieldType.unsigned(end)).valueAt(start)
        );

        if (list==null || list.isEmpty())
        {
            return 0;
        }
        Long aLong = list.get(0);
        if (aLong==null)
        {
            return 0;
        }
        return list.get(0);
    }


    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }



    public <T> void setCacheObjectL(final String key, final T value, final Long timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }


    /**
     * 判断key是否存在 O（1）
     * @param key
     * @return 存在 1，不存在0
     */
    public boolean exists(final String key)
    {
       return redisTemplate.hasKey(key);
    }


    /**
     * 获取key的过期时间,以TimeUnit为单位  -1永久 -2不存在 否则返回具体
     * @param key
     * @param timeUnit
     * @return long
     */
    public Long ttl(final String key,TimeUnit timeUnit)
    {
        return redisTemplate.getExpire(key,timeUnit);
    }



    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }



    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 往set添加元素
     * @param key 键
     * @param value 一个或多个值
     * @param <T>
     * @return long
     */
    public <T> Long setCacheSets(final String key, final T... value)
    {
        Long add = redisTemplate.opsForSet().add(key, value);
        return add;
    }

    /**
     * 往set元素中删除值
     * @param key 键
     * @param value 一个或多个值
     * @param <T>
     * @return
     */
    public <T> Long setRemove(final String key, final T... value)
    {
        Long remove = redisTemplate.opsForSet().remove(key, value);
        return remove;
    }


    /**
     * 两个set进行求交集
     * @param key 键1
     * @param key2 键2
     * @return set
     */
    public <T> Set intersectSet(final String key, final String key2)
    {
        Set<T> intersect = redisTemplate.opsForSet().intersect(key, key2);
        return intersect;
    }


    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);

    }

    /**
     * 获取set值
     * @param key
     * @return
     */
    public String getCacheSetstring(final String key)
    {
        String pop =(String) redisTemplate.opsForSet().pop(key);
        return pop;
    }


    /**
     * 使用管道将map集合元素批量存入redis
     * @param map map集合
     * @param seconds 过期时间
     */
    public void executePipelined(Map<String, String> map, long seconds) {
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
        redisTemplate.executePipelined(new RedisCallback() {

            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                map.forEach((key, value) -> {
                    connection.set(stringSerializer.serialize(key), stringSerializer.serialize(value),Expiration.seconds(seconds), RedisStringCommands.SetOption.UPSERT);
                });
                return null;
            }
        },stringSerializer);
    }

    /**
     *  使用管道将set集合元素批量存入redis
     * @param key 键
     * @param set set集合
     */
    public void executePipelined(final String key,Set<Long> set) {
//        获取序列化对象
        RedisSerializer stringSerializer = redisTemplate.getStringSerializer();
//        执行管道操作
        redisTemplate.executePipelined(new RedisCallback() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                for (Long aLong : set) {
//                    将每一个set元素存入管道
                    connection.set(stringSerializer.serialize(key),stringSerializer.serialize(aLong));
                }
                return null; //回调函数必须返回null
            }
        },stringSerializer);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }

    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     *
     * @param key
     * @param hashKey
     * @param <T>
     * @return
     */
    public <T> Object getCacheMapFeil(final T key,final T hashKey)
    {

        Object o = redisTemplate.opsForHash().get(key, hashKey);
        return o;
    }

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 删除1或多字段  成功删除返回1  删除不存在返回0
     * @param key
     * @param hKey
     * @param value
     * @return
     */
    public Long delCacheMapField(final String key, final String... hKey)
    {
        Long delete = redisTemplate.opsForHash().delete(key, hKey);
        return delete;
    }


    /**
     * 返回给定字段的值。如果给定的字段或 key 不存在时，返回 nil
     * @param key  O(1)
     * @param hKey
     * @return
     */
    public Object getMapFieldValue(final String key, final String hKey)
    {


        Object o = redisTemplate.opsForHash().get(key, hKey);
        return o;
    }



    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 删除Hash中的数据
     * 
     * @param key
     * @param mapkey
     */
    public void delCacheMapValue(final String key, final String hkey)
    {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern)
    {

        return redisTemplate.keys(pattern);
    }


    /**
     *  获取指定位置的集合下标索引，不存在则返回null
     * @param key 键
     * @param value 值
     * @param <T>
     * @return
     */
    public <T> Long getCacheZSet(final String key, final T value)
    {
        Long get = redisTemplate.opsForZSet().rank(key,value);
        return get;
    }


//    ZREVRANGE salary 0 -1 WITHSCORES

    /**
     *  根据指定范围返回最大分数，递减顺序
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @param <T>
     * @return set集合
     */
    public <T> Set<T> getCacheZSetForSco(final String key, final Long start,final Long end)
    {
        Set set = redisTemplate.opsForZSet().reverseRange(key,start,end);
        return set;
    }


    /**
     *  根据指定范围返回最大分数，递减顺序   分页限制limit
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @param <T>
     * @return set集合
     */
    public <T>  Set<T> getZSetRangeByScore(final String key, final double start,final double end,final Long offset,final Long count)
    {

        Set set = redisTemplate.opsForZSet().rangeByScoreWithScores(key, start, end, offset, count);


        return set;
    }



    /**
     *  获得指定元素的分数
     * @param key 键
     * @param value 值
     * @param <T>
     * @return Double
     */
    public <T> Double getCacheZSetScore(final String key, final T value)
    {
        Double score = redisTemplate.opsForZSet().score(key, value);
        return score;
    }



    /**
     *  向集合中插入元素，并设置分数
     * @param key 键
     * @param value 值
     * @param <T>
     * @return
     */
    public <T> Boolean setCacheZSet(final T key, final T value,Long score)
    {
        Boolean add = redisTemplate.opsForZSet().add(key, value, score);

        return add;
    }

    /**
     * 移除指定下标范围的值
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long removeRangeZSet(final String key, final long start,long end)
    {
        Long aLong = redisTemplate.opsForZSet().removeRange(key,start,end);

        return aLong;
    }


    /**
     *  向集合中插入元素，并设置分数
     * @param key 键
     * @param value 值
     * @param <T>
     * @return
     */
    public <T> Boolean MysetCacheZSet(final T key, final T value,Double score)
    {
        Boolean add = redisTemplate.opsForZSet().add(key, value, score);

        return add;
    }




    /**
     * 先有序集合中一次性插入多个元素
     * @param key 键
     * @param value 集合值 new DefaultTypedTuple(“E”,6.0);
     * @param <T>
     * @return long
     */
    public <T> Long setCacheZSet(final String key, final Set<ZSetOperations.TypedTuple> value)
    {

        Long add = redisTemplate.opsForZSet().add(key,value);

        return add;
    }


    public <T> Double incrementScoreZset(final T key, final T value,Integer score)
    {

        Double aDouble = redisTemplate.opsForZSet().incrementScore(key, value, score);
        return aDouble;
    }


    /**
     * 计算集合中元素的数量
     * @param key 键
     * @return 总数
     */
    public <T> Long getCacheZSetZCard(final String key)
    {

        Long sum = redisTemplate.opsForZSet().zCard(key);
        return sum;
    }


    /**
     * 令返回有序集中，指定区间内的成员。
     * @param key
     * @param min
     * @param max
     * @param <T>
     * @return指定区间内，带有分数值(可选)的有序集成员的列表。
     */
    public <T> Set reverseRangeZSetWithScores(final T key,Integer min,Integer max)
    {


            Set<ZSetOperations.TypedTuple> set = redisTemplate.opsForZSet().reverseRangeWithScores(key, min, max);

            return set;

    }






    /**
     * 返回有序集中成员的排名 分数值最大的成员排名为 0
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Long reverseRankZset(final T key,final T value)
    {
        Long aLong = redisTemplate.opsForZSet().reverseRank(key, value);
        return aLong;
    }

    /**
     * 添加指定元素到 HyperLogLog 中。
     * @param key 键
     * @param value 一个或多个值
     * @param <T>
     * @return 整型，如果至少有个元素被添加返回 1， 否则返回 0。
     */
    public <T> Long setCacheHyperLogLog(final String key,T ... value)
    {

        Long add = redisTemplate.opsForHyperLogLog().add(key,value);
        return add;
    }


    /**
     * 获取指定元素在 HyperLogLog 中的数量
     * @param key 键
     * @param <T>
     * @return long
     */
    public <T> Long getCacheHyperLogSize(final String key)
    {

        Long add = redisTemplate.opsForHyperLogLog().size(key);
        return add;
    }


    /**
     * 判断set成员是否存在
     * @param key
     * @param value
     * @param <T>
     * @return
     */
    public <T> Boolean isMemberSet(final String key,final T value)
    {
        Boolean member = redisTemplate.opsForSet().isMember(key, value);
        return member;
    }


//    public void insert(final String key,final String element,int expire){
//        redisTemplate.opsForValue().setBit()
//    }


    /**
     * 添加地理位置成员和坐标
     * @param key
     * @param point 坐标
     * @param member
     * @return
     */
    public Long opsForGeoAdd(final String key, final Point point,Object member)
    {
        Long add = redisTemplate.opsForGeo().add(key, point, member);
        return add;
    }



    public <T> GeoResults radiusGeo(final String key, final Circle circle, RedisGeoCommands.GeoRadiusCommandArgs args)
    {
        GeoResults radius = redisTemplate.opsForGeo().radius(key, circle, args);//1:40分钟
        return radius;
    }



}
